package com.why.cachecloud.configuration;

import com.why.cachecloud.dao.ErrorLogStatDao;
import com.why.cachecloud.entity.ErrorLogStat;
import com.why.cachecloud.entity.TimeBetween;
import com.why.cachecloud.schedule.brevity.BrevityScheduler;
import com.why.cachecloud.schedule.jobs.AppTrafficStatService;
import com.why.cachecloud.schedule.jobs.ScanBigKeyService;
import com.why.cachecloud.service.client.service.AppClientStatisticGatherService;
import com.why.cachecloud.service.client.service.ClientReportCostDistriService;
import com.why.cachecloud.service.client.service.ClientReportValueDistriService;
import com.why.cachecloud.service.custom.InstanceActualStatusService;
import com.why.cachecloud.service.inspect.impl.AppInspectHandler;
import com.why.cachecloud.service.inspect.impl.HostInspectHandler;
import com.why.cachecloud.service.inspect.impl.InstanceStateInspector;
import com.why.cachecloud.service.log.ErrorStatisticsAppender;
import com.why.cachecloud.service.stats.instance.InstanceAlertConfigService;
import com.why.cachecloud.service.task.TaskService;
import com.why.cachecloud.util.ConstUtils;
import com.why.cachecloud.web.service.ConfigService;
import com.why.cachecloud.web.util.DateUtil;
import com.why.cachecloud.web.util.IpUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;

@Slf4j
@Component
public class SchedulerConfig {

    @Resource
    ConfigService configService;

    @Resource
    private ThreadPoolTaskExecutor jvmQuartzThreadPool;

    @Resource
    private ThreadPoolTaskExecutor quartzThreadPool;

    @Resource
    InstanceActualStatusService instanceActualStatusService;

    @Scheduled(fixedDelay = 1000 * 100)
    public void check() {
        jvmQuartzThreadPool.execute(() -> instanceActualStatusService.check());
    }


    /**
     * systemConfigRefreshJobDetail
     * 0/30 * * ? * *
     * SystemConfigRefreshJob.class
     * SystemConfigRefreshJob
     * configService.reloadSystemConfig()
     */
    @Scheduled(cron = "0/30 * * ? * *")
    public void systemConfigRefresh() {
        jvmQuartzThreadPool.execute(() -> configService.reloadSystemConfig());
    }

    /**
     * dispatcherBrevityScheduleTrigger
     * 1 0/1 * ? * *
     * DispatcherBrevityScheduleJob.class
     */

    @Resource
    BrevityScheduler brevityScheduler;

        @Scheduled(cron = "1 0/1 * ? * *")
    public void dispatcherTasks() {
        jvmQuartzThreadPool.execute(() -> brevityScheduler.dispatcherTasks());
    }

    /**
     * errorLogMinuteStatTrigger
     * 0 0/1 * ? * *
     * errorLogMinuteStatJobDetail
     * ErrorLogMinuteStatJob
     */
    @Resource
    ErrorLogStatDao errorLogStatDao;
    @Resource
    IpUtil ipUtil;

     @Scheduled(cron = "0 0/1 * ? * *")
    public void errorLogMinuteStatJob() {
        jvmQuartzThreadPool.execute(this::errorLogMinuteStatJob1);
    }

    public void errorLogMinuteStatJob1() {
        Date collectDate = new Date();
        int port = ipUtil.getLocalPort();
        String ip = ipUtil.getLocalIP();

        Map<String, Long> errorLogMap = ErrorStatisticsAppender.ERROR_NAME_VALUE_MAP.asMap();

        for (Map.Entry<String, Long> entry : errorLogMap.entrySet()) {
            String className = entry.getKey();
            Long currentErrorCount = entry.getValue();
            if (currentErrorCount == null) {
                continue;
            }
            Long lastErrorTotalCount = errorLogStatDao.getLastErrorCount(ip, port, className);
            long diffErrorCount;
            if (lastErrorTotalCount == null || currentErrorCount - lastErrorTotalCount < 0) {
                diffErrorCount = currentErrorCount;
            } else {
                diffErrorCount = currentErrorCount - lastErrorTotalCount;
            }
            if (diffErrorCount == 0) {
                continue;
            }
            ErrorLogStat errorLogStat = new ErrorLogStat();
            errorLogStat.setIp(ip);
            errorLogStat.setPort(port);
            errorLogStat.setClassName(className);
            errorLogStat.setCollectTime(getCollectTime(collectDate));
            errorLogStat.setTotalErrorCount(currentErrorCount);
            errorLogStat.setDiffErrorCount(diffErrorCount);
            Date now = new Date();
            errorLogStat.setCreateTime(now);
            errorLogStat.setUpdateTime(now);

            errorLogStatDao.save(errorLogStat);
        }
    }

    private long getCollectTime(Date collectDate) {
        return NumberUtils.toLong(new SimpleDateFormat("yyyyMMddHHmmss").format(collectDate));
    }


    ///历史的Quartz存储在数据库中的////////////////////////////////////////////////////////////////////////////
    @Resource
    InstanceStateInspector instanceStateInspector;

        @Scheduled(cron = "0 0/10 * ? * *")
    public void instanceStateJob() {
        long startTime = System.currentTimeMillis();
        instanceStateInspector.inspect();
        log.info("InstanceAlertValueJob cost time {} ms", (System.currentTimeMillis() - startTime));
    }

    @Resource
    InstanceAlertConfigService instanceAlertConfigService;

        @Scheduled(cron = "0 0/1 * ? * *")
    public void instanceAlertValueJob() {
        long startTime = System.currentTimeMillis();
        instanceAlertConfigService.monitorLastMinuteAllInstanceInfo();
        log.info("InstanceAlertValueJob cost time {} ms", (System.currentTimeMillis() - startTime));
    }

    @Resource
    TaskService taskService;

        @Scheduled(cron = "0/10 * * ? * *")
    public void taskExecuteJob() {
        long startTime = System.currentTimeMillis();
        taskService.executeNewTask();
        log.info("TaskExecuteJob end, cost time is {} ms", (System.currentTimeMillis() - startTime));
    }

    @Resource
    AppInspectHandler appInspectHandler;

        @Scheduled(cron = "0 0/20 * ? * *")
    public void appInspectorJob() {
        long start = System.currentTimeMillis();
        appInspectHandler.handle();
        log.info("=====================InspectorJob {} Done! cost={} ms=====================", appInspectHandler.getClass().getSimpleName(), (System.currentTimeMillis() - start));
    }

    @Resource
    HostInspectHandler hostInspectHandler;

        @Scheduled(cron = "0 0/5 * ? * *")
    public void hostInspectJob() {
        long start = System.currentTimeMillis();
        hostInspectHandler.handle();
        log.info("=====================InspectorJob {} Done! cost={} ms=====================", hostInspectHandler.getClass().getSimpleName(), (System.currentTimeMillis() - start));
    }


    private static int BATCH_SIZE = 1000;

    private static final String CLEAN_APP_HOUR_COMMAND_STATISTICS = "delete from app_hour_command_statistics where create_time < ? limit " + BATCH_SIZE;

    private static final String CLEAN_APP_MINUTE_COMMAND_STATISTICS = "delete from app_minute_command_statistics where create_time < ? limit " + BATCH_SIZE;

    private static final String CLEAN_APP_HOUR_STATISTICS = "delete from app_hour_statistics where create_time < ? limit " + BATCH_SIZE;

    private static final String CLEAN_APP_MINUTE_STATISTICS = "delete from app_minute_statistics where create_time < ? limit " + BATCH_SIZE;
    /**
     * 清除客户端耗时汇总数据
     */
    private static final String CLEAN_APP_CLIENT_MINUTE_COST_TOTAL = "delete from app_client_costtime_minute_stat_total where collect_time < ? limit " + BATCH_SIZE;

    //清除服务器统计数据
    private static final String CLEAN_SERVER_STAT_STATISTICS = "delete from server_stat where cdate < ? limit " + BATCH_SIZE;

    /**
     * 清除实例基础统计
     */
    private static final String CLEAN_INSTANCE_MINUTE_STATS = "delete from instance_minute_stats where collect_time < ? limit " + BATCH_SIZE;

    @Resource
    JdbcTemplate jdbcTemplate;

    @Resource
    ClientReportCostDistriService clientReportCostDistriService;
    @Resource
    ClientReportValueDistriService clientReportValueDistriService;


        @Scheduled(cron = "0 0 0/1 ? * *")
    public void cleanupDayDimensionalityJob() {
        if (!ConstUtils.WHETHER_SCHEDULE_CLEAN_DATA) {
            log.info("whether_schedule_clean_data is false , ignored");
            return;
        }
        try {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(new Date());

            // 清除应用&命令统计数据(保存31天)
            calendar.add(Calendar.DAY_OF_MONTH, -31);
            Date time = calendar.getTime();
            long cleanCount = 0;
            cleanCount = scrollDelete(CLEAN_APP_HOUR_COMMAND_STATISTICS, time);
            log.info("clean_app_hour_command_statistics count={}", cleanCount);
            cleanCount = scrollDelete(CLEAN_APP_MINUTE_COMMAND_STATISTICS, time);
            log.info("clean_app_minute_command_statistics count={}", cleanCount);
            cleanCount = scrollDelete(CLEAN_APP_HOUR_STATISTICS, time);
            log.info("clean_app_hour_statistics count={}", cleanCount);
            cleanCount = scrollDelete(CLEAN_APP_MINUTE_STATISTICS, time);
            log.info("clean_app_minute_statistics count={}", cleanCount);

            //清除服务器统计数据
            calendar.setTime(new Date());
            calendar.add(Calendar.DAY_OF_MONTH, -7);
            String date = new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime());
            cleanCount = scrollDelete(CLEAN_SERVER_STAT_STATISTICS, date);
            log.info("clean_server_stat_total count={}", cleanCount);

            long timeFormat = NumberUtils.toLong(new SimpleDateFormat("yyyyMMddHHmm00").format(calendar.getTime()));
            //清除进程级别统计数据(保存5天)
            long start = System.currentTimeMillis();
            timeFormat = NumberUtils.toLong(new SimpleDateFormat("yyyyMMddHHmm").format(DateUtils.addDays(new Date(), -5)));
            cleanCount = scrollDelete(CLEAN_INSTANCE_MINUTE_STATS, timeFormat);
            log.info("clean_instance_minute_stats timeFormat={} count={} cost={}s", timeFormat, cleanCount, (System.currentTimeMillis() - start) / 1000);

            //清除客户端耗时数据(保存2天)
            calendar.setTime(new Date());
            calendar.add(Calendar.DAY_OF_MONTH, -2);
            timeFormat = NumberUtils.toLong(new SimpleDateFormat("yyyyMMddHHmm00").format(calendar.getTime()));
            cleanCount = clientReportCostDistriService.deleteBeforeCollectTime(timeFormat);
            log.info("clean_app_client_costtime_minute_stat count={}", cleanCount);

            //清除客户端耗时汇总数据(保存14天)
            calendar.setTime(new Date());
            calendar.add(Calendar.DAY_OF_MONTH, -14);
            timeFormat = NumberUtils.toLong(new SimpleDateFormat("yyyyMMddHHmm00").format(calendar.getTime()));
            cleanCount = jdbcTemplate.update(CLEAN_APP_CLIENT_MINUTE_COST_TOTAL, timeFormat);
            log.info("clean_app_client_costtime_minute_stat_total count={}", cleanCount);

            //清除客户端值数据(保存2天)
            calendar.setTime(new Date());
            calendar.add(Calendar.DAY_OF_MONTH, -2);
            timeFormat = NumberUtils.toLong(new SimpleDateFormat("yyyyMMddHHmm00").format(calendar.getTime()));
            cleanCount = clientReportValueDistriService.deleteBeforeCollectTime(timeFormat);
            log.info("clean_app_client_value_minute_stats count={}", cleanCount);

        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 滚动删除表数据
     */
    private long scrollDelete(String sql, Object time) {
        long totalCount = 0;
        while (true) {
            int cleanCount = jdbcTemplate.update(sql, time);
            totalCount += cleanCount;
            if (cleanCount == 0) {
                break;
            }
        }
        return totalCount;
    }

    /////CleanupDayAppClientStatJob

    private static final String CLEAN_APP_CLIENT_COMMAND_MINUTE_STATISTICS = "delete from app_client_command_minute_statistics where current_min < ?";
    private static final String CLEAN_APP_CLIENT_EXCEPTION_MINUTE_STATISTICS = "delete from app_client_exception_minute_statistics where current_min < ?";
    private static final String CLEAN_APP_CLIENT_LATENCY_COMMAND = "delete from app_client_latency_command where create_time < ?";
    private static final String CLEAN_APP_CLIENT_STATISTIC_GATHER = "delete from app_client_statistic_gather where gather_time < ?";
    private static final String CLEAN_INSTANCE_LATENCY_HISTORY = "delete from instance_latency_history where execute_date < ?";

        @Scheduled(cron = "0 0 10 * * ?")
    public void cleanupDayAppClientStatJob() {
        if (!ConstUtils.WHETHER_SCHEDULE_CLEAN_DATA) {
            log.info("whether_schedule_clean_data is false , ignored");
            return;
        }
        try {
            log.info("begin-CleanupDayAppClientStatJob");
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(new Date());
            calendar.add(Calendar.DAY_OF_MONTH, -14);
            long timeFormat = NumberUtils.toLong(new SimpleDateFormat("yyyyMMddHHmm00").format(calendar.getTime()));
            String date = new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime());

            /**
             * 清除命令统计&异常统计（保存14天）
             */
            int cleanCount = jdbcTemplate.update(CLEAN_APP_CLIENT_COMMAND_MINUTE_STATISTICS, timeFormat);
            log.info("clean_app_client_command_minute_statistics count={}", cleanCount);
            cleanCount = jdbcTemplate.update(CLEAN_APP_CLIENT_EXCEPTION_MINUTE_STATISTICS, timeFormat);
            log.info("clean_app_client_exception_minute_statistics count={}", cleanCount);
            cleanCount = jdbcTemplate.update(CLEAN_APP_CLIENT_LATENCY_COMMAND, calendar.getTime());
            log.info("clean_app_client_latency_command count={}", cleanCount);
            cleanCount = jdbcTemplate.update(CLEAN_APP_CLIENT_STATISTIC_GATHER, date);
            log.info("clean_app_client_statistic_gather count={}", cleanCount);
            cleanCount = jdbcTemplate.update(CLEAN_INSTANCE_LATENCY_HISTORY, timeFormat);
            log.info("clean_instance_latency_history count={}", cleanCount);

            log.info("end-CleanupDayAppClientStatJob");
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }


    @Resource
    AppClientStatisticGatherService appClientStatisticGatherService;

        @Scheduled(cron = "0 0 1 * * ?")
    public void reviseAppClientStatisGatherJob() {
        TimeBetween timeBetween = fillWithDateFormat();
        long startTime = timeBetween.getStartTime();
        long endTime = timeBetween.getEndTime();
        appClientStatisticGatherService.bathSave(startTime, endTime);
        log.info("end-reviseAppClientStatisGatherJob, startTime={} endTime:{}", startTime, endTime);
    }

    private final static String COLLECT_TIME_FORMAT = "yyyyMMdd000000";

    private TimeBetween fillWithDateFormat() {
        Date endDate = new Date();
        Date startDate = DateUtils.addDays(endDate, -1);
        long startTime = NumberUtils.toLong(DateUtil.formatDate(startDate, COLLECT_TIME_FORMAT));
        long endTime = NumberUtils.toLong(DateUtil.formatDate(endDate, COLLECT_TIME_FORMAT));
        return new TimeBetween(startTime, endTime, startDate, endDate);
    }

    ////gatherAppClientStatisticsJob

        @Scheduled(cron = "0 0/5 * ? * *")
    public void gatherAppClientStatisticsJob() {
        Date endDate = DateUtils.addMinutes(new Date(), -5);
        Date startDate = DateUtils.addMinutes(endDate, -5);
        long startTime = NumberUtils.toLong(DateUtil.formatDate(startDate, COLLECT_TIME_FORMAT));
        long endTime = NumberUtils.toLong(DateUtil.formatDate(endDate, COLLECT_TIME_FORMAT));
        TimeBetween timeBetween = new TimeBetween(startTime, endTime, startDate, endDate);
        startTime = timeBetween.getStartTime();
        endTime = timeBetween.getEndTime();

        appClientStatisticGatherService.bathAdd(startTime, endTime);
        log.info("end-gatherAppClientStatisticsJob, startTime={} endTime:{}", startTime, endTime);
    }

    //cleanupMinuteDimensionalityJob

    private static final String CLEAN_STANDARD_STATISTICS = "delete from standard_statistics where collect_time < ?";

        @Scheduled(cron = "0 0/10 * ? * *")
    public void cleanupMinuteDimensionalityJob() {
        quartzThreadPool.execute(() -> {
            //清除进程级别统计数据(保存最近10分钟)
            long start = System.currentTimeMillis();
            Date date = DateUtils.addMinutes(new Date(), -10);
            long timeFormat = NumberUtils.toLong(new SimpleDateFormat("yyyyMMddHHmm").format(date));
            int cleanCount = jdbcTemplate.update(CLEAN_STANDARD_STATISTICS, timeFormat);
            log.info("clean_standard_statistics timeFormat={} count={} cost:{} ms", timeFormat, cleanCount, (System.currentTimeMillis() - start));
        });
    }


    ///maintainBrevitySchedulerJob
//    @Scheduled(cron = "30 0/1 * ? * *")

    //    @Resource
//    BrevityScheduler brevityScheduler;
//    @XxlJob("maintainBrevitySchedulerJob")
    public void maintainBrevitySchedulerJob() {
        quartzThreadPool.execute(() -> brevityScheduler.maintainTasks());
    }

    /**
     * 大key扫描
     */
    @Resource
    ScanBigKeyService scanBigKeyService;

//    @XxlJob("scanBigKeyJob")
    //大Key扫描
    @Scheduled(cron = "0 0 0/1 * * ?")
    public void scanBigKeyJob() {
        scanBigKeyService.scanBigKey();
    }


    /**
     * 流量统计,每天凌晨进行
     */
    @Resource
    private AppTrafficStatService appTrafficStatService;

//    @XxlJob("trafficStat")
    public void trafficStatJob() {
        appTrafficStatService.trafficStat();
    }

    /**
     * 小时内的流量比对,每小时进行
     */
//    @XxlJob("checkAppTrafficJob")
    public void checkAppTraffic() {
        appTrafficStatService.checkAppTraffic();
    }
}